﻿using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Web;

using Encoding = System.Text.Encoding;

namespace Outfish {

	/// <summary>
	/// Url-Encodes the given post data in the application/x-www-form-urlencoded format. 
	/// See http://www.w3.org/TR/html401/interact/forms.html#h-17.13.4.1 for more information.
	/// </summary>
	public class FormUrlPostData : IPostData {
		
		#region constructor
	
		/// <summary>
		/// Creates an empty NameValueCollection form data.
		/// Handy for not having to include System.Collections.Specialized.
		/// </summary>
		public FormUrlPostData(){
			this._postData = new NameValueCollection();
		}
	
		public FormUrlPostData( NameValueCollection postData ){
			if( postData == null ){ throw new ArgumentNullException("postData"); }
			this._postData = postData;
		}
		
		#endregion

		/// <summary>
		/// Get the bytes to be used as the WebRequest body
		/// </summary>
		public byte[] PostBytes {
			get {
				return FormUrlPostData.Encode( this._postData );
			}
		}
		
		/// <summary>
		/// Returns "application/x-www-form-urlencoded" for use in header.
		/// </summary>
		public string ContentType {
			get { return "application/x-www-form-urlencoded"; }
		}
		
		public NameValueCollection Collection{ get{ return this._postData; } }
		
		public override string ToString(){
			return  this.ContentType +"\r\n" + FormUrlPostData.GetQueryString( this._postData );
		}
		
		#region private
		
		private NameValueCollection _postData;

		#endregion

		/// <summary>
		/// Encodes the post information in the application/form-url-encoded format
		/// </summary>
		/// <param name="postData">The post data to encode.</param>
		/// <returns>The encoded post data.</returns>
		static public byte[] Encode(NameValueCollection postData) {
			
			if(postData.Count == 0){ return new byte[0]; }
			
			string postDataString = GetQueryString( postData );
			
			const int WesternEuropeanWindowsCodePage = 1252;
			return Encoding.GetEncoding( WesternEuropeanWindowsCodePage )
				.GetBytes( postDataString );
		}

		/// <summary>
		/// converts the name-value collection to a string that can be "?" appended to the url
		/// </summary>
		/// <param name="nameValueCollection"></param>
		/// <returns></returns>
		static public string GetQueryString( NameValueCollection nameValueCollection ){
			return ImplodeKeysAndValues(nameValueCollection).Join("&");
		}
		
		/// <summary>
		/// Converts a "?" query string to the Name-value pair collection.
		/// </summary>
		/// <param name="queryString"></param>
		/// <returns></returns>
		static public NameValueCollection GetFormCollection( string queryString ){
			System.Collections.Specialized.NameValueCollection parameters = new System.Collections.Specialized.NameValueCollection();
			if( string.IsNullOrEmpty( queryString ) ){ return parameters; } // empty

			// tear off the leading '?' if it is there
			if( queryString[1] == '?' ){ queryString = queryString.Substring(1); }
			
			foreach( string pair in queryString.Split('&',';')){
				string[] parts = pair.Split('=');
				string key = parts[0].DecodeUrl();
				string value = parts[1].DecodeUrl();
				parameters[ key ] = value;
			}
			
			return parameters;
		}
		
		/// <summary>
		/// Having this exposed assists in unit testing.  
		/// But you probably want to use GetQueryString instead
		/// </summary>
		/// <param name="nameValueCollection"></param>
		/// <remarks>null values are ignored</remarks>
		/// <returns></returns>
		static public List<string> ImplodeKeysAndValues(NameValueCollection nameValueCollection) {
			List<string> key_value = new List<string>(nameValueCollection.Count);
			for(int i = 0; i < nameValueCollection.Count; ++i) {
				string name = nameValueCollection.GetKey(i);
				string[] values = nameValueCollection.GetValues(i);
				if( values == null ){ continue; }
				foreach(string value in values ) {
					// nulls are ignored
					if( value != null ){ 
						string pair = HttpUtility.UrlEncode(name) + "=" + HttpUtility.UrlEncode(value);
						key_value.Add( pair );
					}
				}
			}
			return key_value;
		}

	}
	
	
}
